package com.dhy.qigsaw.splash

import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentActivity
import com.dhy.compatbundle.defaultQigsawSplitVersionFile
import com.dhy.qigsaw.splash.ApiUtil.api
import com.dhy.qigsaw.splash.data.SplitInfoUpdateUtil
import com.dhy.versionchecker.Rx3Wrapper
import com.dhy.versionchecker.VersionUtil
import com.dhy.versionchecker.isNetworkConnected
import com.dhy.xintent.*
import com.google.android.play.core.splitinstall.SplitInstallManagerFactory
import com.google.android.play.core.splitinstall.SplitInstallRequest
import com.google.android.play.core.splitinstall.SplitInstallSessionState
import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener
import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus
import com.iqiyi.android.qigsaw.core.common.CompatBundle
import io.reactivex.rxjava3.schedulers.Schedulers
import kotlinx.android.synthetic.main.fragment_splash.*
import org.json.JSONObject


abstract class BaseSplashFragment : ISplashFragment() {
    private var moduleNames: List<String> = emptyList()
    private val installStateListener = SplitInstallStateUpdatedListener { state ->
        Log.e(TAG, "SplitInstallStateUpdatedListener error, ${state.errorCode()}: ${state.status()}")
        when (state.status()) {
//                SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION -> onRequiresUserConfirmation(state)
//                SplitInstallSessionStatus.CANCELING -> {                }
//                SplitInstallSessionStatus.CANCELED -> {                }
//                SplitInstallSessionStatus.PENDING -> onPending(state)
            SplitInstallSessionStatus.DOWNLOADING -> onDownloading(state)
//                SplitInstallSessionStatus.DOWNLOADED -> onDownloaded()
//                SplitInstallSessionStatus.INSTALLING -> onInstalling()
            SplitInstallSessionStatus.INSTALLED -> onAllModuleInstalled()
            SplitInstallSessionStatus.FAILED -> onFailed("下载模块失败", true)
//            else -> {            }
        }
    }
    private val installManager by lazy { SplitInstallManagerFactory.create(ownerActivity)!! }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        installManager.registerListener(installStateListener)
        XIntent.with(requireArguments()).apply {
            moduleNames = readExtraList()
        }
        startInstall()
    }

    private fun startInstall() {
        if (installManager.isAllInstalled()) return onAllModuleInstalled()

        ownerActivity.apply {
            Waterfall.flow {
                if (defaultQigsawSplitVersionFile.exists() && defaultQigsawSplitVersionFile.length() > 0) next()
                else downloadSplitInfo(ownerActivity, this)
            }.flow {
                val request = SplitInstallRequest.newBuilder().addModules(moduleNames).build()
                installManager.startInstall(request)
                    .addOnFailureListener {
                        onFailed(it.message, true)
                    }
            }
        }
    }

    private val splitInfo by lazy { SplitInfoUpdateUtil(ownerActivity) }
    private fun downloadSplitInfo(activity: Activity, flow: Flow) {
        ApiUtil.checkSplitUpdate(activity) {
            val url = it?.url
            if (splitInfo.isNew(url)) {
                splitInfo.updateQigsawSplits(url!!) { compat ->
                    if (compat == true) {
                        splitInfo.save(false)
                        moduleNames = activity.updateQigsawConfigDynamicFeatures() ?: moduleNames
                        flow.next()
                    } else {
                        val msg = if (compat == false) "配置不兼容，请更新应用" else "下载配置错误"
                        onFailed(msg, true)
                    }
                }
            } else {
                if (url != null) onFailed("No update found")
                else onFailed("No record found", true)
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        installManager.unregisterListener(installStateListener)
        dialog?.dismiss()
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_splash, container, false)
    }

    private fun onDownloading(state: SplitInstallSessionState) {
        Log.i("TAG", "bytesDownloaded ${state.bytesDownloaded()}/${state.totalBytesToDownload()}")
        val percent = (state.bytesDownloaded() * 100 / state.totalBytesToDownload())
        progressBar.progress = percent.toInt()
        tvProgress.formatText(percent)
        progressBar.show()
        tvProgress.show()
    }

    private var dialog: Dialog? = null
    abstract fun onAllModuleInstalled()

    private var retryCount = 0
    private fun onFailed(msg: String?, retry: Boolean = false) {
        if (retry && retryCount++ < 2 && ownerActivity.isNetworkConnected) {
            return startInstall()
        }

        dialog?.dismiss()
        Log.i("TAG", msg ?: "")
        val append = if (msg.isNullOrEmpty()) "" else "\n" + msg
        dialog = AlertDialog.Builder(ownerActivity)
            .setCancelable(false)
            .setTitle("提示")
            .setMessage("更新数据失败，请重试！$append")
            .setNegativeButton("关闭") { dialog, _ ->
                dialog.dismiss()
                ownerActivity.finish()
            }.setPositiveButton("重试") { dialog, _ ->
                retryCount = 0
                dialog.dismiss()
                startInstall()
            }.show()
        onError(append)
    }

    open fun onError(msg: String) {

    }

    private var mActivity: FragmentActivity? = null

    /**
     *应用升级时，获取安装权限后，返回时 全局变量容易变为 NULL。
     * */
    protected val ownerActivity: FragmentActivity
        get() {
            return if (mActivity != null) mActivity!!
            else requireActivity()
        }

    override fun startSplash(activity: AppCompatActivity, dynamicFeatures: List<String>, containerViewId: Int) {
        mActivity = activity
        checkAppVersion(activity)
        if (installManager.isAllInstalled()) {
            checkSplitVersion()
            return onAllModuleInstalled()
        }
        val arguments = Bundle()
        XIntent.with(arguments).putSerializableExtra(dynamicFeatures)
        this.arguments = arguments
        activity.supportFragmentManager.beginTransaction()
            .add(containerViewId, this)
            .commitNow()
    }

    private fun checkAppVersion(activity: Activity) {
        VersionUtil.checkVersion(activity, Rx3Wrapper { api.checkAppVersion() }, false, null)
    }

    private fun checkSplitVersion() {
        api.checkSplitVersion().subscribeOn(Schedulers.io())
            .subscribe({
                if (splitInfo.isNew(it.url)) {
                    splitInfo.updateQigsawSplits(it.url) { ok ->
                        if (ok == true) splitInfo.save(true, it.isForceUpdate)
                    }
                }
            }, {
                it.printStackTrace()
            })
    }

    companion object {
        private const val TAG = "SplashFragment"
    }
}

/**
 * 配置插件变更（增加或减少插件）
 * @return newSplits if updated
 * */
fun Context.updateQigsawConfigDynamicFeatures(): List<String>? {
    val compat = CompatBundle.instance
    val config = compat?.readDefaultSplitVersionContent(this, "")
    if (config != null) {
        val newSplits = config.toSplitNameList()

        val field = compat.qigsawConfigClass().getDeclaredField("DYNAMIC_FEATURES")
        field.isAccessible = true
        val splits = (field.get(null) as Array<*>).toSet()
        if (splits != newSplits.toSet()) {
            field.set(null, newSplits.toTypedArray())
            return newSplits
        }
    }
    return null
}

private fun String.toSplitNameList(): List<String> {
    val splitNames: MutableList<String> = mutableListOf()
    val json = JSONObject(this)
    val splits = json.getJSONArray("splits")
    for (i in 0 until splits.length()) {
        splitNames.add(splits.getJSONObject(i).getString("splitName"))
    }
    return splitNames.sorted()
}